//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//-----------------------------------------------------------------------------
// File: DDrawTest.cpp
//
// Desc: DDrawTest is a DirectDraw sample application that demonstates the
//       use of video overlay. It creates a flipable overlay, loads a small
//       text over the Camera output. Press F12 or ESC to quit.
//       
//
//-----------------------------------------------------------------------------

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include "stdafx.h"
#include <windows.h>
#include <ddraw.h>
#include <winbase.h>
#include <objbase.h>
#include <commctrl.h>

//-----------------------------------------------------------------------------
// Local definitions
//-----------------------------------------------------------------------------
#define NAME                TEXT("DDrawTestWndClass")
#define TITLE               TEXT("DDrawTest")


#define	TEXT_COLOR						RGB(0xFF,0x00,0x00)
#define	TEXT_BG_COLOR					RGB(0xFF,0xFF,0xFF)

//-----------------------------------------------------------------------------
// Default settings
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Global data
//-----------------------------------------------------------------------------
LPDIRECTDRAW                g_pDD = NULL;        // DirectDraw object
LPDIRECTDRAWSURFACE         g_pDDSPrimary = NULL; // Primary Surface.
LPDIRECTDRAWSURFACE         g_pDDSOverlay = NULL; // The overlay primary.
LPDIRECTDRAWSURFACE         g_pDDSBackOverlay = NULL; // The overlay primary.
BOOL                        g_bActive = FALSE;   // Is application active?
int                         g_RotationAngles = 0; // Supported rotation angles.
int                         g_CurrentAngle = 0;   // Current rotation angle.
BOOL bSurfaceEnumerated = FALSE;


// Our instance handle.

HINSTANCE g_hInstance;

//-----------------------------------------------------------------------------
// Local data
//-----------------------------------------------------------------------------


// These are the pixel formats this app supports.  Most display adapters
// with overlay support will recognize one or more of these formats.
// We start with YUV format, then work down to RGB. (All 16 bpp.)

static DDPIXELFORMAT ddpfOverlayFormats[] = {
    //{sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y','U','Y','V'),0,0,0,0,0},  // YUYV
    //{sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('U','Y','V','Y'),0,0,0,0,0},  // UYVY
    //{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16,  0x7C00, 0x03e0, 0x001F, 0},        // 16-bit RGB 5:5:5
    {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16,  0xF800, 0x07e0, 0x001F, 0}         // 16-bit RGB 5:6:5
};

#define PF_TABLE_SIZE (sizeof(ddpfOverlayFormats) / sizeof(ddpfOverlayFormats[0]))

static RECT rs;
static RECT rd;

//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
void ReleaseAllObjects(void);
HRESULT InitFail(HWND, HRESULT, LPCTSTR, ...);
HRESULT RestoreAllSurfaces();
long FAR PASCAL WindowProc(HWND, UINT, WPARAM, LPARAM);
HRESULT WINAPI EnumSurfacesCallback(LPDIRECTDRAWSURFACE, LPDDSURFACEDESC, LPVOID);
HRESULT EnumerateBackBuffer();
HRESULT InitApp(HINSTANCE hInstance, int nCmdShow);

//-----------------------------------------------------------------------------
// Name: ReleaseAllObjects()
// Desc: Finished with all objects we use; release them
//-----------------------------------------------------------------------------
static void
ReleaseAllObjects(void)
{
    if (g_pDDSOverlay != NULL)
    {
        // Use UpdateOverlay() with the DDOVER_HIDE flag to remove an overlay 
	    // from the display.
	    g_pDDSOverlay->UpdateOverlay(NULL, g_pDDSPrimary, NULL, DDOVER_HIDE, NULL);
        g_pDDSOverlay->Release();
        g_pDDSOverlay = NULL;
    }

    if (g_pDDSPrimary != NULL)
    {
        g_pDDSPrimary->Release();
        g_pDDSPrimary = NULL;
    }

    if (g_pDD != NULL)
    {
        g_pDD->Release();
        g_pDD = NULL;
    }
}

//-----------------------------------------------------------------------------
// Name: InitFail()
// Desc: This function is called if an initialization function fails
//-----------------------------------------------------------------------------
#define PREFIX      TEXT("DDrawTest: ")
#define PREFIX_LEN  10

static HRESULT
InitFail(HWND hWnd, HRESULT hRet, LPCTSTR szError,...)
{
    TCHAR                       szBuff[128] = PREFIX;
    va_list                     vl;

    va_start(vl, szError);
    //wvsprintf(szBuff + PREFIX_LEN, szError, vl);
    wsprintf(szBuff + wcslen(szBuff), TEXT("\r\n"));
    ReleaseAllObjects();
    OutputDebugString(szBuff);
    //DestroyWindow(hWnd);
    va_end(vl);
    return hRet;
}

#undef PREFIX_LEN
#undef PREFIX



//-----------------------------------------------------------------------------
// Name: RestoreAllSurfaces
// Desc: Called in case we lose our surface's vram.
//-----------------------------------------------------------------------------
static HRESULT 
RestoreAllSurfaces()
{
    HRESULT hRet;

    // Try Restoring the primary surface.

    hRet = g_pDDSPrimary->Restore();
    if (hRet != DD_OK)
	    return hRet;

    // Try Restoring the overlay surface.

    hRet = g_pDDSOverlay->Restore();
    if (hRet != DD_OK)
	    return hRet;

    // Show the overlay.

    hRet = g_pDDSOverlay->UpdateOverlay(&rs, g_pDDSPrimary, &rd, DDOVER_SHOW, NULL);
    return hRet;
}
//-----------------------------------------------------------------------------
// Name: FlipOverlay
// Desc: This function is used to flip between the primary overlay surface and 
//		 corresponding back buffer.
//-----------------------------------------------------------------------------
void FlipOverlay()
{
	HRESULT hRet;
	// Flip.

    while (TRUE)
    {
        hRet = g_pDDSOverlay->Flip(NULL, 0);
        if (hRet == DD_OK)
            break;
        if (hRet == DDERR_SURFACELOST)
        {
            hRet = RestoreAllSurfaces();
            if (hRet != DD_OK)
                break;
        }
        if (hRet != DDERR_WASSTILLDRAWING)
            break;
    }
}
//-----------------------------------------------------------------------------
// Name: EraseScreen
// Desc: This function will erase the content of the Back Buffer
//-----------------------------------------------------------------------------
BOOL EraseScreen( ) {
    DDBLTFX     ddbltfx;
    HRESULT     ddrval;

	if(g_pDDSBackOverlay == NULL)
		return FALSE;

    // Erase the background
    memset (&ddbltfx, 0, sizeof (ddbltfx));
    ddbltfx.dwSize = sizeof( ddbltfx );
    ddbltfx.dwFillColor = 0;

    while( 1 )
    {
        ddrval = g_pDDSOverlay->Blt(NULL, NULL, NULL, DDBLT_COLORFILL, &ddbltfx );

        if( ddrval == DD_OK )
        {
            break;
        }
        if( ddrval == DDERR_SURFACELOST )
        {
			if( !g_pDDSOverlay->Restore() )
                return 1;
        }
        if( ddrval != DDERR_WASSTILLDRAWING )
        {
            return 1;
        }
    }
	FlipOverlay();
	return TRUE;
}
//-----------------------------------------------------------------------------
// Name: DrawTextInOverlaySurface
// Desc: It gets the device context handle for the Overlay surface backbuffer and using that 
// we can do whatever we want to do in the HDC.
//-----------------------------------------------------------------------------

BOOL DrawTextInOverlaySurface(TCHAR *msg)
{
	HDC hDC;
	HRESULT hRet;
	int textWnd_X,textWnd_Y;
	
    hRet = g_pDDSBackOverlay->GetDC(&hDC);
    if (hRet != DD_OK)
	{
		NKDbgPrintfW(L"UpdateResString: GetDC Failed!\r\n");
        return FALSE;
	}

	SetBkColor(hDC, TEXT_BG_COLOR );
	SetTextColor(hDC, TEXT_COLOR);
	SetBkMode(hDC, TRANSPARENT);

	textWnd_X = (GetSystemMetrics(SM_CXSCREEN)/2) - (CAMERA_PREVIEW_WIDTH/2) + 20;
	textWnd_Y = (GetSystemMetrics(SM_CYSCREEN)/2) - (CAMERA_PREVIEW_HEIGHT/2) + 36;

	ExtTextOut(hDC, 
		       textWnd_X, 
		       textWnd_Y,
		       0,                        // fuOptions
		       NULL,                     // lprc
               msg, 
		       lstrlen(msg),
		       NULL);                    // lpDx

	
	g_pDDSBackOverlay->ReleaseDC(hDC);

	FlipOverlay();
	return TRUE;
}
//-----------------------------------------------------------------------------
// Name: WindowProc()
// Desc: The Main Window Procedure
//-----------------------------------------------------------------------------
long FAR PASCAL
WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
	case WM_CREATE:
		return 1;
#ifdef UNDER_CE
        case WM_ACTIVATE:
#else
        case WM_ACTIVATEAPP:
#endif
            // Pause if minimized or not the top window
            g_bActive = (wParam == WA_ACTIVE) || (wParam == WA_CLICKACTIVE);
            return 0L;

        case WM_KILLFOCUS:
            // We do not allow anyone else to have the keyboard focus until
            // we are done.
            SetFocus(hWnd);
            return 0L;

        case WM_DESTROY:
            // Clean up and close the app
            ReleaseAllObjects();
			ReleaseAllDshowObjects();
            PostQuitMessage(0);
            return 0L;

        case WM_KEYDOWN:
            // Handle any non-accelerated key commands
            switch (wParam)
            {
				case 'D':
					DrawTextInOverlaySurface(L"Testing");
					return 0L;
				case 'F':
					FlipOverlay();//Flips between Back and Primary surfaces
					return 0L;
				case 'E':
					EraseScreen();	//Erase the surface buffer and flip it to Primary surface				
					return 0L;
                case VK_ESCAPE:
                case VK_F12:
                    PostMessage(hWnd, WM_CLOSE, 0, 0);
                    return 0L;              
            }
            break;        
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

//-----------------------------------------------------------------------------
// Name: EnumSurfacesCallback()
// Desc: Used by EnumerateBackBuffer to aid it loading all three bug images.
//-----------------------------------------------------------------------------
static HRESULT WINAPI 
EnumSurfacesCallback(LPDIRECTDRAWSURFACE lpDDSurface,  
                     LPDDSURFACEDESC lpDDSurfaceDesc,  
                     LPVOID lpContext)
{
if (!bSurfaceEnumerated) {

        *((LPDIRECTDRAWSURFACE *)lpContext) = lpDDSurface;
        bSurfaceEnumerated = TRUE;
        return DDENUMRET_OK;
    }
    else {
        //Require only one surface so we are releasing other surfcace if enumerated.
        lpDDSurface->Release();
        return DDENUMRET_CANCEL;
    }
}



//-----------------------------------------------------------------------------
// Name: EnumerateBackBuffer()
// Desc: Enumerate the back buffer.
//-----------------------------------------------------------------------------
static HRESULT
EnumerateBackBuffer()
{
    HRESULT hRet;
    
	// Use the enumeration attachment function to load the other images.
    hRet = g_pDDSOverlay->EnumAttachedSurfaces((LPVOID)&g_pDDSBackOverlay,EnumSurfacesCallback);
    return (hRet);
}

//-----------------------------------------------------------------------------
// Name: InitApp()
// Desc: Create and intialize the DirectDraw Overlay surface
//       
//-----------------------------------------------------------------------------
static HRESULT
InitApp(HWND hWnd, int nCmdShow)
{
    DDSURFACEDESC               ddsd;
    DDCAPS                      ddcaps;
    HRESULT                     hRet;
    DWORD                       dwUpdateFlags = 0;
    DDOVERLAYFX                 ovfx;
       


    // Create the main DirectDraw object

    hRet = DirectDrawCreate(NULL, &g_pDD, NULL);
    if (hRet != DD_OK)
        return InitFail(NULL, hRet, TEXT("DirectDrawCreate FAILED"));

    // Get normal mode.

    hRet = g_pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
    if (hRet != DD_OK)
        return InitFail(NULL, hRet, TEXT("SetCooperativeLevel FAILED"));

    // Get a primary surface interface pointer (only needed for init.)

    memset(&ddsd, 0, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
    hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);
    if (hRet != DD_OK)
        return InitFail(NULL, hRet, TEXT("CreateSurface FAILED"));

    // See if we can support overlays.

    memset(&ddcaps, 0, sizeof(ddcaps));
    ddcaps.dwSize = sizeof(ddcaps);
    hRet = g_pDD->GetCaps(&ddcaps, NULL);
    if (hRet != DD_OK)
        return InitFail(NULL, hRet, TEXT("GetCaps FAILED"));

    if (ddcaps.dwOverlayCaps == 0)
        return InitFail(NULL, hRet, TEXT("Overlays are not supported in hardware!"));

    // Get alignment info to compute our overlay surface size.

    rs.left = 0;
    rs.top = 0;
	rs.right = GetSystemMetrics(SM_CXSCREEN);
    rs.bottom = GetSystemMetrics(SM_CYSCREEN);
    if (ddcaps.dwAlignSizeSrc != 0)
	    rs.right += rs.right % ddcaps.dwAlignSizeSrc;
    
    // Create the overlay flipping surface. We will attempt the pixel formats
    // in our table one at a time until we find one that jives.

    memset(&ddsd, 0, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_FLIP;
    ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_BACKBUFFERCOUNT | 
                   DDSD_PIXELFORMAT;
	ddsd.dwWidth = rs.right;
	ddsd.dwHeight = rs.bottom;
    ddsd.dwBackBufferCount = 1;

    int i = 0;
    do {
	    ddsd.ddpfPixelFormat = ddpfOverlayFormats[i];
	    hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSOverlay, NULL);
    } while (hRet != DD_OK && (++i < PF_TABLE_SIZE));

    if (hRet != DD_OK)
        return InitFail(NULL, hRet, TEXT("Unable to create overlay surface!"));

    // Load the images.

    if (EnumerateBackBuffer() != DD_OK)
        return InitFail(NULL, hRet, TEXT("Unable to load images to overlay surface!"));

    // Finish setting up the overlay.

    int StretchFactor1000 = ddcaps.dwMinOverlayStretch > 1000 ? ddcaps.dwMinOverlayStretch : 1000;

    rd.left=0; 
    rd.top=0;
    // Adding 999 takes care of integer truncation problems.
    rd.right  = (rs.right * StretchFactor1000 + 999) / 1000;
    rd.bottom = rs.bottom * StretchFactor1000 / 1000;
    if (ddcaps.dwAlignSizeDest != 0)
	    rd.right = (int)((rd.right + ddcaps.dwAlignSizeDest - 1)/ ddcaps.dwAlignSizeDest) *
                    ddcaps.dwAlignSizeDest;

    // Set the flags we'll send to UpdateOverlay
    dwUpdateFlags = DDOVER_SHOW;

    // Does the overlay hardware support source color keying?
    // If so, we can hide the black background around the image.
    // This probably won't work with YUV formats
    memset(&ovfx, 0, sizeof(ovfx));
    ovfx.dwSize = sizeof(ovfx);
    if (ddcaps.dwOverlayCaps & DDOVERLAYCAPS_CKEYSRC)
    {
        dwUpdateFlags |= DDOVER_KEYSRCOVERRIDE;

        // Create an overlay FX structure so we can specify a source color key.
        // This information is ignored if the DDOVER_SRCKEYOVERRIDE flag 
        // isn't set.
        ovfx.dckSrcColorkey.dwColorSpaceLowValue=0; // black as the color key
        ovfx.dckSrcColorkey.dwColorSpaceHighValue=0;
    }

    // Update the overlay parameters.

    hRet = g_pDDSOverlay->UpdateOverlay(&rs, g_pDDSPrimary, &rd, dwUpdateFlags, &ovfx);
    if (hRet != DD_OK)
        return InitFail(NULL, hRet, TEXT("Unable to show overlay surface!"));

    return DD_OK;
}

//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: Initialization, message loop
//-----------------------------------------------------------------------------
int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
#ifdef UNDER_CE
        LPWSTR lpCmdLine,
#else
        LPSTR lpCmdLine,
#endif
        int nCmdShow)
{
    MSG                         msg;
	HWND                        hWnd;
    WNDCLASS                    wc;
	RECT						rcWorkArea;

	CoInitializeEx(NULL, COINIT_MULTITHREADED);

    g_hInstance = hInstance;

	    // Set up and register window class.

    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;//LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON));
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH )GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = NAME;
    RegisterClass(&wc);

    // Create a window.
    // Now, create the main window
	SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);

	hWnd = CreateWindow(NAME, TITLE, WS_VISIBLE | WS_SYSMENU | WS_OVERLAPPED,
		rcWorkArea.left,
            rcWorkArea.top,
            rcWorkArea.right - rcWorkArea.left,
            rcWorkArea.bottom - rcWorkArea.top,NULL, NULL, hInstance, NULL);
    if (!hWnd)
        return FALSE;


	SetFocus(hWnd);

	//Build the DirectShow Camera Filter graph For rendering the video on the screen
	BuildCaptureGraph(hWnd);

	//Run the Filter graph to start the streaming.
	RunCaptureGraph();
	
    if (InitApp(hWnd, nCmdShow) != DD_OK)
        return FALSE;

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

	CoUninitialize();
    return msg.wParam;
}
